package demo.aop;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LogAspect {
	@Pointcut("execution(* demo.aop.*.*(..))")
	public void testPointCut() {
	}

	@Around("testPointCut()")
	public Object doAround(ProceedingJoinPoint point) throws Throwable {
		try {
			Object target = point.getTarget();
			String methodName = point.getSignature().getName();
			Object[] args = point.getArgs();
			Class[] parameterTypes = ((MethodSignature) point.getSignature())
					.getMethod().getParameterTypes();
			Method m = null;
			m = getMethod(target, methodName, parameterTypes, args);
			Log logged=m.getAnnotation(Log.class);
			if(logged!=null){
				System.out.println(logged.value());
				System.out.println(logged.level());
			}
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		Object object = point.proceed();
		return object;
	}

	/*
	 * @Before("testPointCut()") public void doBefore() {
	 * System.out.println("doBefore"); }
	 * 
	 * @After("testPointCut()") public void doAfter() {
	 * System.out.println("doAfter"); }
	 */
	public Method getMethod(Object target, String methodName,
			Class[] parameterTypes, Object[] args) throws SecurityException,
			NoSuchMethodException {
		Method m = null;
		m = target.getClass().getMethod(methodName, parameterTypes);
		if (m.isBridge()) {
			for (int i = 0; i < args.length; i++) {
				Class genClazz = getSuperClassGenricType(target.getClass());
				if (args[i].getClass().isAssignableFrom(genClazz)) {
					parameterTypes[i] = genClazz;
				}
			}
			m = target.getClass().getMethod(methodName, parameterTypes);
		}
		return m;
	}

	public Class getSuperClassGenricType(Class clazz) {
		Type genType = clazz.getGenericSuperclass();
		Type[] types = ((ParameterizedType) genType).getActualTypeArguments();
		if (!(types[0] instanceof Class)) {
			return Object.class;
		}
		return (Class) types[0];
	}
}
